/****************************************************************\

Copyright 2004 Enzo Michelangeli

This file is part of the KadC library.

KadC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

KadC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with KadC; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

In addition, closed-source licenses for this software may be granted
by the copyright owner on commercial basis, with conditions negotiated
case by case. Interested parties may contact Enzo Michelangeli at one
of the following e-mail addresses (replace "(at)" with "@"):

 em(at)em.no-ip.com
 em(at)i-t-vision.com

\****************************************************************/

#define DEBUG 0
#include <config.h>
#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <time.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <net.h>
#include <sys/utsname.h>
#include <sys/ioctl.h>
#ifdef HAVE_SYS_SYSCTL_H
#include <sys/sysctl.h>
#endif
#include <net/if.h>
#define min(a,b) (((a)<(b))?(a):(b))
#include <errno.h>

#include <KadClog.h>
#include <rbt.h>

// This is the changed architecture to fit on dvn
#include "kademliamain.h"


extern int h_errno;

#define TRIVIAL_LOCAL_ADDR	"127.0.0.1"
#define MAX_NUM_INTERFACES	3
#define IFNAME_LEN		256

typedef struct _ip_port {
	unsigned long int ip;
	unsigned short int port;
	time_t expiry;
} ip_port;

static dvn_net_udp_send_callback g_udp_send_func = NULL;
dvn_net_udp_send_callback dvn_net_set_udp_send_callback(
	dvn_net_udp_send_callback udp_send_func) {

	dvn_net_udp_send_callback old_udp_send_func = g_udp_send_func;
	g_udp_send_func = udp_send_func;
	return old_udp_send_func;
}


//////////////////////////////////////////////////
//
// When started in dvn, the kademlia library will call this function
//
/////////////////////////////////////////////////
void dvn_net_init(
		  char* addr,
		  char* port,
		  int bootstrap_needed,
		  in_addr_t bootstrap_ip,
		  uint16_t bootstrap_port,
		  void** curNodeMem,
		  struct timeval t
		     ){
        //sets the time for logging purposes
  	KadClog_set_current_dvn_time(t);

	// Call the init routine in the kademliamain.c
	kademliaMain_init(addr, port, bootstrap_needed, bootstrap_ip, bootstrap_port, curNodeMem);
	return;
}

///////////////////////////////////////////////
//
// When DVN reports a message, this should be called to handle the packet
//
//
///////////////////////////////////////////////
void dvn_net_onrecv(
		    void * modmem,          /* module's memory block */
		    in_addr_t from,         /* who the message is from */
		    uint16_t source_port,
		    unsigned int data_size, /* data size */
		    unsigned char * data,   /* actual data */
		    struct timeval t        /* current time */
		    ){
	
	// [dfk]:  (deprecated) retrieve the "pul" pointer to the udp receiver from the modmem and pass it to this function
        //        the variable "myUDP" in the current object is the pointer to the "pul" variable

        // [dfk] We pass the modmem variable that contains all the node's state to the kademlia message processor

        //sets the time for logging purposes
  	KadClog_set_current_dvn_time(t);

        // Calls the kademlia main function to do the translation from void* to CKademlia class.  It will eventually call this file again.
        kademliaMain_onrecv(modmem,
			data, 
			data_size, 
			from,
			4013);//We don't have a concept of port, so the entire network uses the same port: 0xFAD 
	
	  return;
}


///////////////////////////////////////////////
//
// When DVN reports a periodic check, this should be called to process any packets in the queue
//
//
///////////////////////////////////////////////
int dvn_net_on_process(void *modmem, struct timeval t){

  int sleep_until = 0;
  //sets the time for logging purposes
  KadClog_set_current_dvn_time(t);
  
  sleep_until = kademliaMain_onProcess(modmem, t.tv_sec);
  return sleep_until;
}

void dvn_net_on_destroy(void *modmem, struct timeval t) {
  KadClog_set_current_dvn_time(t);
  kademliaMain_onDestroy(modmem, t.tv_sec);
}

/* compare ip_port structures by ip address, then port*/
static int ip_port_eq(ip_port *ippt1, ip_port *ippt2) {
	return (ippt1->ip == ippt2->ip && ippt1->port == ippt2->port);
}

static int ip_port_lt(ip_port *ippt1, ip_port *ippt2) {
	if(ippt1->ip != ippt2->ip)
		return (ippt1->ip < ippt2->ip);
	else
		return (ippt1->port < ippt2->port);	/* if ip is the same, compare ports */
}

int node_is_blacklisted(UDPIO *pul, unsigned long int ip, unsigned short int port) {
	ip_port ippt, *val;
	int is_bl, missingsecs;
	void *iter;

	ippt.ip = ip;
	ippt.port = port;
	//pthread_mutex_lock(&pul->mutex);	/* \\\\\\ LOCK \\\\\\ */
	is_bl = (rbt_find(pul->blacklist, &ippt, &iter) == RBT_STATUS_OK);
	if(is_bl) {
		val = rbt_value(iter);
		missingsecs = val->expiry -  KadClog_get_current_dvn_time();
		if(missingsecs <= 0) {	                   /* expired? */
			is_bl = 0;	                   /* then it ain't blacklisted */
			rbt_erase(pul->blacklist, iter);   /* remove record */
			free(val);			   /* destroy it as well */
		} else {
			is_bl = missingsecs;
		}
	}
	//pthread_mutex_unlock(&pul->mutex);	/* ///// UNLOCK ///// */
	return is_bl;
}

int node_blacklist(UDPIO *pul, unsigned long int ip, unsigned short int port, int howmanysecs) {
	ip_port *pippt, *val;
	int was_bl, missingsecs;
	void *iter;

	pippt = malloc(sizeof(ip_port));
	assert(pippt != NULL);
	pippt->ip = ip;
	pippt->port = port;
	pippt->expiry =  KadClog_get_current_dvn_time() /*time(NULL)*/ + howmanysecs;
	pthread_mutex_lock(&pul->mutex);	/* \\\\\\ LOCK \\\\\\ */
	was_bl = (rbt_find(pul->blacklist, pippt, &iter) == RBT_STATUS_OK);
	if(was_bl) {
		val = rbt_value(iter);
		missingsecs = val->expiry -  KadClog_get_current_dvn_time();//time(NULL);
		if(missingsecs <= 0) 	        /* expired? */
			was_bl = 0; 	        /* then it wasn't really blacklisted anymore... */
		else
			was_bl = missingsecs;
		val->expiry = pippt->expiry;	/* anyway just update expiry */
		free(pippt);			/* we don't need the new one anymore */
	} else {	                        /* no entry in table, yet? */
		rbt_insert(pul->blacklist, pippt, pippt, 0);	/* add to blacklist */
	}
	pthread_mutex_unlock(&pul->mutex);	/* ///// UNLOCK ///// */
	return was_bl;
}

int node_unblacklist(UDPIO *pul, unsigned long int ip, unsigned short int port) {
	ip_port ippt, *val;
	int was_bl, missingsecs;
	void *iter;

	ippt.ip = ip;
	ippt.port = port;
	pthread_mutex_lock(&pul->mutex);	/* \\\\\\ LOCK \\\\\\ */
	was_bl = (rbt_find(pul->blacklist, &ippt, &iter) == RBT_STATUS_OK);
	if(was_bl) {
		val = rbt_value(iter);
		missingsecs = val->expiry -  KadClog_get_current_dvn_time();//time(NULL);
		if(missingsecs <= 0) 	/* expired? */
			was_bl = 0;	/* then it wasn't really blacklisted anymore... */
		else
			was_bl = missingsecs;
		rbt_erase(pul->blacklist, iter);	/* anyway, remove record */
		free(val);
	}
	pthread_mutex_unlock(&pul->mutex);	/* ///// UNLOCK ///// */
	return was_bl;
}

int node_blacklist_load(UDPIO *pul, FILE *inifile) {
	int cnt = 0;
	int ntok;
	char *p, line[256];
	char dqip[64];
	unsigned int port;
	int expiry, ttl;
	for(;;) {
		p = fgets(line, sizeof(line)-1, inifile);
		if(p == NULL)
			break;
		if(strchr(p, '[') != NULL)	/* if we just read new section start... */
			break;
		ntok = sscanf(line, "%63s%u%d", dqip, &port, &expiry);
		if(ntok != 3) {
#ifdef DEBUG
			KadC_log(ERROR_PRINT,"Ignoring malformed line: %s\n", line);
#endif
			continue;
		}
		if(dqip[0] == '#')
			continue;	/* ignore comments */
		ttl = expiry -  KadClog_get_current_dvn_time();//time(NULL);
		if(ttl <= 0) {	/* if already expired... */
#ifdef DEBUG
			KadC_log(ERROR_PRINT,"Ignoring expired blacklisting for %s:%u\n", dqip, port);
#endif
			continue;				/* ...then ignore this entry */
		}
#ifdef DEBUG
		KadC_log(ERROR_PRINT,"Blacklisted %s:%u will be snubbed for %d s\n", dqip, port, ttl);
#endif
		/* also domain names are OK here, besides dotted quads */
		node_blacklist(pul, domain2hip(dqip), port, ttl);
		cnt++;
	}
	return cnt;
}

int node_blacklist_dump(UDPIO *pul, FILE *wfile) {
	int cnt = 0;
	void *iter;
	pthread_mutex_lock(&pul->mutex);	/* \\\\\\ LOCK \\\\\\ */
	for(iter = rbt_begin(pul->blacklist);iter != NULL; iter=rbt_next(pul->blacklist, iter)) {
		ip_port *val = rbt_value(iter);
		assert(val != NULL);
		cnt++;
	}
	pthread_mutex_unlock(&pul->mutex);	/* ///// UNLOCK ///// */
	return cnt;
}

int node_blacklist_purge(UDPIO *pul, int unconditional) {
	int cnt = 0;
	pthread_mutex_lock(&pul->mutex);	/* \\\\\\ LOCK \\\\\\ */
	for(;;) {
		ip_port *val;
		void *iter = rbt_begin(pul->blacklist);
		if(iter == NULL)
			break;
		val = rbt_value(iter);
		if(unconditional || (val->expiry -  KadClog_get_current_dvn_time() /*time(NULL)*/ <= 0)) {
			rbt_erase(pul->blacklist, iter);
			free(val);
			cnt++;
		}
	}
	pthread_mutex_unlock(&pul->mutex);	/* ///// UNLOCK ///// */
	return cnt;
}


int udp_recv_packet(
		    UDPIO *pul,
		    unsigned char* inBuf, 
		    int inBufLen, 
		    unsigned long int src_ip,
		    unsigned int src_port) {


		/* NOTE: if a datagram longer than t->size arrives,
		   the buffer is only filled in with the first t->size
		   characters; however:
		   - With Cygwin, the whole datagram size is returned
		     in nrecv, WITHOUT TRUNCATION
		   - With -mno-cygwin, nrecv returns -1
		 */
		int status;
		int bl_seconds;
		udpmsg *pum;
		unsigned long int remoteip;
		unsigned short int remoteport;
		unsigned char *buf;
		int nrecv;

		//KadC_logt(GENERAL_PRINT,"%i <- %i: %i bytes, ", pul->localip, src_ip, inBufLen);// We leave the string unterminated so that we can put the message type

		buf = calloc(1, inBufLen);
		if(buf == NULL) {
			KadC_log(ERROR_PRINT, "%s:%d: failed to allocate memory for buffer\n", __FILE__, __LINE__);
			return 0;
		}
		
		// kad_lib:  Instead of doing a recvfrom, we copy the message that we got from the network
		memcpy(buf, inBuf, inBufLen);
		nrecv = inBufLen;
		
		if(nrecv > pul->bufsize - 1)
			nrecv = -1;	/* in UNIX as in WIN32 ignore oversize datagrams */
		if(nrecv <= 0) {/* ...catch oversize datagrams */
			KadC_log(ERROR_PRINT, "Incoming buffer from %i too large: %i bytes\n", src_ip, nrecv);
			goto next_iteration;
		}

		remoteip = src_ip;//ntohl(remote.sin_addr.s_addr);
		remoteport = src_port;//ntohs(remote.sin_port);
		if((bl_seconds = node_is_blacklisted(pul, remoteip, remoteport)) != 0) {
#ifdef DEBUG
			KadC_log(ERROR_PRINT,"*** Discarding datagram from node %s:%u blacklisted for %d more seconds\n",
						htoa(pul->remoteip), pul->remoteport, bl_seconds);
#endif
			goto next_iteration;
		}

		pum = calloc(1, sizeof(udpmsg));
		if(pum == NULL) {
			goto next_iteration;
		}

		pum->buf = malloc(nrecv);
		if(pum->buf == NULL) {
			free(pum);
			goto next_iteration;
		}

		pum->nrecv = nrecv;
		pum->remoteip = remoteip;
		pum->remoteport = remoteport;
		memcpy(pum->buf, buf, nrecv);


		/* enqueue the message for udp_proc_thread */
		status = pul->udp_recv_fifo->enq(pul->udp_recv_fifo, pum);

		if(status == 0) {
			/* if enq succeeded, just iterate (consumer thread will destroy message) */
			/*KadC_logt(VERBOSE_PRINT,"got a message and queued it successfully\n");*/
			goto next_iteration;
		} else {
			KadC_logt(ERROR_PRINT,"Error Queuing message\n");

			KadC_logt(ERROR_PRINT,"%s:%d ** UDP message enq failed: %d input datagram lost!\n", __FILE__, __LINE__, status);
			free(pum->buf);
			free(pum);	/* if enq failed, destroy message */
		}
next_iteration:

		free(buf);	/* in case of any errors, destroy old buffer */

	//
	// When libKad gets here, it means that the packet is all queued up and we are ready to process it.
	// Since libkad doesn't have a main loop to process packets, we process it immediately

	// Grab the current packet and process it.  It is typically done by invoking a member function of a class
	// Since we are in C, we might have to figure out something different


	       
		//KadC_logt(GENERAL_PRINT,"%i: receive packet ok.\n", pul->localip, src_ip, inBufLen);

		return 0x0;
}

/* wait on queue for messages containing UDP data, length and
   source IP and port; then copy them to UDPIO and, if required,
   call callback */
/*
  static void *udp_proc_thread(void *arg) {
*/
void udp_proc_packet(void *arg){
	UDPIO *pul = arg;
	udpmsg *pum;

	//while(pul->fd != -1) {
	pum = pul->udp_recv_fifo->deqtw(pul->udp_recv_fifo, 500);
	if(pum == NULL) {
	  KadC_logt(ERROR_PRINT, "%i: failed to copy incoming udp packet to UDPIO\n", pul->localip);
	  return;
	  //continue;
	}
	//printf("Preprocessing packet from the udp layer, %i bytes\n", pum->nrecv);
	
	pul->nrecv = pum->nrecv;
	pul->remoteip = pum->remoteip;
	pul->remoteport = pum->remoteport;
	pul->buf = (unsigned char *)pum->buf;
	free(pum);
	
	if(pul->callback[pul->buf[0]] != NULL)
	  (*pul->callback[pul->buf[0]])(pul);
	free(pul->buf);
	pul->buf = NULL;
	//}
}

/* Creates a UDP socket bound to a specific interface and port
   (if the interface is 0, it binds to any).
   Sets the *pfd parameter used for recvfrom()/send() I/O
   Creates a separate thread to wait for inbound packets

   Associates to the socket an optional callback function
   called by the recvfrom()'ing thread when a datagram is
   received. A buffer containing the data is passed
   to that function; it won't be rewritten until
   that callback will return (as the callback routine
   belongs to the same thread issuing recvfrom() ).

   Returns 0, or errno/WSAGetLastError()

 */

int startUDPIO(UDPIO *pul) {
        //int fd, on = 1;
	struct sockaddr_in local;
	static pthread_mutex_t mutex_initializer = PTHREAD_MUTEX_INITIALIZER;

	pul->blacklist = rbt_new((rbtcomp *)&ip_port_lt, (rbtcomp *)&ip_port_eq);
	pul->remoteip = 0;
	pul->remoteport = 0;
	pul->mutex = mutex_initializer;
	pul->udp_recv_fifo = new_queue(10);	/* max outstanding UDP packets */
	memset(&(local), 0, sizeof(struct sockaddr_in));
	local.sin_family = AF_INET;
	local.sin_addr.s_addr = htonl(pul->localip);
	local.sin_port = htons(pul->localport);

	pul->fd = 0;
	// kad_lib: We don't need the thread anymore.  There is a callback when a packet is received
	// We only need to let the callback know where to enqueue the packets
	//g_net_pul = pul;
	//pthread_create(&pul->_udp_recv, NULL, udp_recv_thread, pul);

	return 0;

	//err_ret:

	return errno;
}

int stopUDPIO(UDPIO *pul) {
	int status;
	udpmsg *pum;

	status = close(pul->fd);
	pul->fd = -1;	/* tell the udp_recv and udp_proc threads that it's time to go... */
	/*pthread_join(pul->_udp_proc, NULL);	 ensure the termination of udp_proc_thread */

	if(pul->buf != NULL)
		free(pul->buf);
	/* cleanup any leftover in the udp message queue */
	while((pum = pul->udp_recv_fifo->deqtw(pul->udp_recv_fifo, 500)) != NULL) {
		free(pum->buf);
		free(pum);
	}
	pul->udp_recv_fifo->destroy(pul->udp_recv_fifo);
	node_blacklist_purge(pul, 1);	/* unconditionally empty blacklist */
	rbt_destroy(pul->blacklist);	/* destroy rbt */

	return status;
}

/* Call: domain2hip("some.domain.com");
   returns IP address in host byte order, or 0 if the domain name
   can't be resolved.
   Made MT safe through mutex locking.
 */

unsigned long int domain2hip(const char *domain) {
	static pthread_mutex_t __mutex = PTHREAD_MUTEX_INITIALIZER;
	struct hostent *hp;
	unsigned long int hip;	/* ip in host network order */


	if(domain[0] >= '0' && domain[0] <= '9') {
		hip = ntohl(inet_addr(domain));
	} else {

		pthread_mutex_lock(&__mutex);	/* begin critical area */
		hp = gethostbyname(domain);
		if(hp == NULL) {
#ifdef DEBUG
			KadC_log(ERROR_PRINT,"\nCan't resolve hostname %s\n", domain);
#endif
			hip = 0xffffffff;
		} else {
			hip = ntohl(((unsigned long int *)(*hp->h_addr_list))[0]);
		}
		endhostent();
		pthread_mutex_unlock(&__mutex);	/* end critical area */
	}


	return hip;

}

static char *inet_ntoa_r(char *ip, struct in_addr sin_addr) {
	unsigned char *b = (unsigned char *)&sin_addr;
	sprintf(ip, "%d.%d.%d.%d", b[0], b[1], b[2], b[3]);
	return ip;
}

/* blocking send() */
int UDPsend(UDPIO *pul, unsigned char *buf, int buflen, unsigned long int destip, int destport) {
	int status;
	int x;
	struct sockaddr_in destsockaddr;
	int fd = pul->fd;
	/*KadC_logt(VERBOSE_PRINT,"Sending a packet of size %i to %s : %i\n",buflen,htoa(destip),destport);*/
	//KadC_logt(GENERAL_PRINT,"%i -> %i: %i bytes\n", pul->localip, destip, buflen);
	//KadC_logt(VERBOSE_PRINT,"outgoing data = ");
	KadC_logt_parsed(buf, pul->localip);/*Prints the type of packet being sent*/
	for(x=0; x<buflen; x++) {
		KadC_log(VERBOSE_PRINT,"%X:",(int)buf[x]);
	}
	KadC_log(VERBOSE_PRINT, "\n");
	memset(&destsockaddr, 0, sizeof(struct sockaddr_in));
	destsockaddr.sin_family = AF_INET;
	destsockaddr.sin_port   = htons((unsigned short int)destport);
	destsockaddr.sin_addr.s_addr = htonl(destip);

	/* this lock protects the fd from concurrent access
	   by separate threads either via sendto() recvfrom() */

	//[dfk]:  replaced the code here to send it via DVN

	//pthread_mutex_lock(&pul->mutex);	/* \\\\\\ LOCK UDPIO \\\\\\ */
	/*
	status = sendto(fd, (char *)buf, buflen,
		0, (struct sockaddr *)&destsockaddr,
		(socklen_t)sizeof(destsockaddr));
	*/
	/* only send if we have a function for sending.  Ignore if we don't */
	if (g_udp_send_func) {
		(*g_udp_send_func)(destip, destport, buf, buflen);
	}
	status = 1;
	//pthread_mutex_unlock(&pul->mutex);	/* ///// UNLOCK UDPIO ///// */

	return status;
}
/* end blocking send() */

#if 0
char *htoa(unsigned long int ip) {
	struct in_addr ia;
	ia.s_addr = htonl(ip);
	return inet_ntoa(ia);
}
#else

char *htoa(unsigned long int ip) {
	static char s[32];	/* yeah, non-reentrant, and so is inet_ntoa()... */
	struct in_addr ia;
	ia.s_addr = htonl(ip);
	return inet_ntoa_r(s, ia);
}
#endif

/* returns true if the ip address is non-routable */
int isnonroutable(unsigned long int ip) {
#define q2b(a, b, c, d) (((a)<<24)+((b)<<16)+((c)<<8)+(d))
	return
			((ip & q2b(255,  0,0,0)) == q2b( 10,  0,0,0)) ||
 			((ip & q2b(255,  0,0,0)) == q2b(127,  0,0,0)) ||
			((ip & q2b(255,240,0,0)) == q2b(172, 16,0,0)) ||
			((ip & q2b(255,255,0,0)) == q2b(192,168,0,0)) ||
			( ip                     == q2b(    0,0,0,0)) ||
			( ip                     == q2b(255,255,255,255));
}

/* returns true if the ip address (in host byte order) is assigned to a local interface */
int is_a_local_address(unsigned long int ip) {
	int sockfd;
	int status;
	struct sockaddr_in my_addr;

	if(ip == (unsigned long int)(-1))
		return 0;	/* here it means "inet_addr error", not "broadcast"! */
	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	memset(&(my_addr), 0, sizeof(struct sockaddr_in));
	my_addr.sin_family = AF_INET;
	my_addr.sin_addr.s_addr = htonl(ip);
	my_addr.sin_port = 0; 				/* any free port */
	status = bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr));

	close(sockfd);
	return (status >= 0);

}


/***************************************************************************
 * 
 * Purpose: Get IP address of local machine by ioctl on eth0-ethk
 * 
 * Return: As an unsigned long in network byte order
 *
 **************************************************************************/
uint32_t get_local_addr_eth(void)
{
  int i, tempfd;
  struct sockaddr_in addr;
  char ifname[IFNAME_LEN];
  struct ifreq ifr;		
  
  for (i = 0; i < MAX_NUM_INTERFACES; i++) {
    sprintf(ifname, "eth%d", i);
    strcpy(ifr.ifr_name, ifname);
    tempfd = socket(AF_INET, SOCK_DGRAM, 0);
    
    if (-1 != ioctl(tempfd, SIOCGIFFLAGS, (char *)&ifr)) {
      if (0 != (ifr.ifr_flags & IFF_UP)) {
	if (-1 != ioctl(tempfd, SIOCGIFADDR, (char *)&ifr)) {
	  addr = *((struct sockaddr_in *) &ifr.ifr_addr);
	  close(tempfd);
	  return addr.sin_addr.s_addr;
	}
      }
    }
    close(tempfd);
  }
  
  return inet_addr(TRIVIAL_LOCAL_ADDR);
}





/***************************************************************************
 * 
 * Purpose: Get IP address of local machine by uname/gethostbyname
 * 
 * Return: As an unsigned long in network byte order
 *
 **************************************************************************/
uint32_t get_local_addr_uname(void)
{
  struct utsname myname;
  uint32_t addr;

  if (uname(&myname) < 0) {
    KadC_logt(ERROR_PRINT,"uname failed:");
  } else {
    addr = name_to_addr(myname.nodename);
  }
  
  if (addr == 0)
    return inet_addr(TRIVIAL_LOCAL_ADDR);
  else
    return addr;
}


/***************************************************************************
 * 
 * Purpose: Get IP address of local machine
 * 
 * Return: As an unsigned long in network byte order
 *
 **************************************************************************/
uint32_t get_local_addr(void)
{
  uint32_t addr;
  
  /* First try uname/gethostbyname */
  if ((addr = get_local_addr_uname()) != inet_addr(TRIVIAL_LOCAL_ADDR))
    return addr;
  /* If that is unsuccessful, try ioctl on eth interfaces */
  if ((addr = get_local_addr_eth()) != inet_addr(TRIVIAL_LOCAL_ADDR))
    return addr;
  
  /* This is hopeless, return TRIVIAL_IP */
  return(inet_addr(TRIVIAL_LOCAL_ADDR));
}


/***************************************************************************
 * 
 * Purpose: Get the IP address of an arbitrary machine
 *	given the name of the machine
 * 
 * Return: As an unsigned long in network byte order
 *
 **************************************************************************/
uint32_t name_to_addr(const char *name)
{
    int i;
    struct hostent *hptr = gethostbyname(name);
    if (!hptr) {
      KadC_logt(ERROR_PRINT,"gethostbyname(%s) failed", name);
    }
    else {
      for (i = 0; i < hptr->h_length/sizeof(uint32_t); i++) {
	uint32_t addr = *((uint32_t *) hptr->h_addr_list[i]);
	if (inet_addr(TRIVIAL_LOCAL_ADDR) != addr)
	  return addr;
      }
    }
    return 0;
}

